<?php
/**
 * 客户端请求
 * +-----------------------------
 * User: BOBO
 * +-----------------------------
 * Date: 2021/10/9
 * +-----------------------------
 * Time: 14:06
 * +-----------------------------
 * Created by PhpStorm.
 * +-----------------------------
 * Copyright (c) 2020~2031
 * +-----------------------------
 */

namespace PDDCore;


class TopClient
{
    /**
     * 客户端ID
     * @var string
     */
    public $appkey;

    /**
     * 客户端秘钥
     * @var string
     */
    public $secret;

    /**
     * 网关API地址
     * @var string
     */
    public $gatewayUrl = "https://gw-api.pinduoduo.com/api/router";

    /**
     * 响应格式，即返回数据的格式，JSON或者XML（二选一），默认JSON，注意是大写
     * @var string
     */
    public $format = "JOSN";

    /**
     * API协议版本号，默认为V1
     * @var string
     */
    public $version = "v1";

    /**
     * UNIX时间戳，单位秒，需要与拼多多服务器时间差值在10分钟内
     * @var
     */
    public $timestamp;

    /**
     * 通过code获取的access_token(无需授权的接口，该字段不参与sign签名运算)
     * @var
     */
    public $accessToken;

    /**
     * 构造函数
     * TopClient constructor.
     * @param string $appkey
     * @param string $secret
     */
    public function __construct(string $appkey,string $secret)
    {
        $this->appkey = $appkey;
        $this->secret = $secret;
    }

    /**
     * @param string $gatewayUrl
     */
    public function setGatewayUrl($gatewayUrl)
    {
        $this->gatewayUrl = $gatewayUrl;
    }

    /**
     * @return mixed
     */
    public function getAppkey()
    {
        return $this->appkey;
    }

    /**
     * 执行结果
     * @param $request 请求数据
     * @param null $accessToken 临时token
     * @param string $dataType 数据请求类型，例：JSON，file【文件上传】不区分大小写
     * @return array|mixed
     */
    public function execute($request, $accessToken=null,$dataType="")
    {
        $result = new ResultSet();
        try{
            // 验证基础规则
            $request->check();
        }catch (\Exception $e){
            return [
                "error_response"=>[
                    "error_msg"=>$e->getMessage(),
                    "sub_msg"=>$e->getMessage(),
                    "sub_code"=>$e->getCode(),
                    "error_code"=>$e->getCode(),
                    "request_id"=>date("YmdHis")
                ]
            ];
        }
        // 获取业务参数
        $apiParams = $request->getApiParas();
        $apiName = $request->getApiMethodName();
        // 拼接公共参数
        $sysParams = $this->sysCommonParams($apiName,$accessToken);
        // 生成签名
        $sysParams['sign'] = $this->generateSign(array_merge($apiParams,$sysParams));
        // 拼接URL
        $this->httpBuildRequest($sysParams,$query);
        $requestUrl = $this->gatewayUrl."?".$query;
        // 请求接口
        $res = $this->requestCurl(
            $requestUrl,"post",$apiParams,$dataType
        );
        return $res;
    }

    /**
     * 公共业务参数
     * @param string $apiName 接口名
     * @param null $accessToken 临时token
     * @return array
     */
    protected function sysCommonParams(string $apiName,$accessToken=null)
    {
        $sysParams = [
            "type"=>$apiName,
            "client_id"=>$this->appkey,
            "timestamp"=>time(),
            "data_type"=>$this->format,
            "version"=>$this->version
        ];
        if ($accessToken !== null){
            $sysParams['access_token'] = $accessToken;
        }
        return $sysParams;
    }

    /**
     * 生成签名
     * @param array $param 公共参数和业务参数
     * @return string
     */
    protected function generateSign(array $param)
    {
        ksort($param);
        $plainText = $this->secret;
        foreach ($param as $key=>$val){
            // 是否标量
            if (is_scalar($val) && "@" != substr($val, 0, 1)){
                $plainText .= $key.$val;
            }
        }
        $plainText .= $this->secret;
        return strtoupper(md5($plainText));
    }

    /**
     * HTTP公共请求参数转query字符串
     * @param array $param 公共业务参数
     * @param string $query http请求query字符串
     * @return string
     */
    protected function httpBuildRequest(array $param,&$query='')
    {
        foreach ($param as $key=>$val){
            $query .= $key ."=".urlencode($val)."&";
        }
        $query = rtrim($query,"&");
        return $query;
    }

    /**
     * 发起请求
     * @param string  $url 请求的url
     * @param string $method  请求方法
     * @param $data 请求参数
     * @param string $type //请求数据的类型
     * @param array $header // 请求的head头
     * @return array|mixed
     */
    protected function requestCurl($url, $method, $data, $type = '', $header = [])
    {
        $method = strtoupper($method);
        $ch = curl_init();
        // 验证是否文件上传
        if ('file' == $type)
        {
            foreach ($data as $k => $v)
            {
                if("@" == substr($v, 0, 1))//判断是不是文件上传
                {
                    //文件上传用multipart/form-data，否则用www-form-urlencoded
                    if(class_exists('\CURLFile')){
                        $data[$k] = new \CURLFile(substr($v, 1));
                    }
                    continue;
                }
            }
            unset($k, $v);
            if (class_exists('\CURLFile')) {
                curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
            } else {
                if (defined('CURLOPT_SAFE_UPLOAD')) {
                    curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
                }
            }
        }
        curl_setopt($ch, CURLOPT_URL, trim($url));
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        if ($method != 'GET') {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        }
        $arrHeader = array(
            'Accept-Language: zh-cn',
            'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)',
            'Cache-Control: no-cache',
        );
        $contentType = [];
        if ($type) {
            switch (strtolower($type)) {
                case 'xml' :
                    $dataType = 'text/xml';
                    break;
                case 'text':
                    $dataType = 'text/plain';
                    break;
                case 'json':
                    $dataType = 'application/json';
                    break;
                case "file":
                    $dataType = 'multipart/form-data';
                    break;
                default :
                    $dataType = 'application/json';
            }
            $contentType = ['Content-type: ' . $dataType . ';charset=utf-8'];
        }
        $arr = array_merge($arrHeader,$contentType,$header);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $arr);
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            $errorMsg = 'curl error :'.curl_error($ch);
            $logData = [
                'error' => $errorMsg,
                'url' => $url,
                'method' => $method,
                'params' => $data,
                'time' => date('Y-m-d H:i:s')
            ];
            $this->logCommunicationError(
                $data['type'],$url,500,$logData
            );
            curl_close($ch); // 关闭CURL会话
            return [
                "error_response"=>[
                    "error_msg"=>"接口报错，服务异常~",
                    "sub_msg"=>$errorMsg,
                    "sub_code"=>500,
                    "error_code"=>500,
                    "request_id"=>date("YmdHis")
                ]
            ];
        }
        curl_close($ch); // 关闭CURL会话
        return empty($response)? [] : json_decode($response, true);
    }

    /**
     * 记录日志通信错误
     * @param $apiName 接口名
     * @param $requestUrl 请求URL
     * @param $errorCode 错误code
     * @param $responseTxt
     */
    protected function logCommunicationError($apiName, $requestUrl, $errorCode, $responseTxt)
    {
        $localIp = isset($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_ADDR"] : "CLI";
        $logger = new TopLogger;
        $logger->conf["log_file"] = rtrim(TOP_SDK_WORK_DIR, '\\/') . '/' . "logs/top_comm_err_" . $this->appkey . "_" . date("Y-m-d") . ".log";
        $logger->conf["separator"] = "^_^";
        $logData = array(
            date("Y-m-d H:i:s"),
            $apiName,
            $this->appkey,
            $localIp,
            PHP_OS,
            $this->version,
            $requestUrl,
            $errorCode,
            str_replace("\n","",$responseTxt)
        );
        $logger->log($logData);
    }

}